<?php
#############################################################
## Name          : debuglib for PHP5
## Author        : Thomas Schüßler <debuglib at atomar dot de>
## Last changed  : 31.03.2009 10:15:13
## Revision      : 45
## Website       : http://phpdebuglib.de
############################################################


/*
 * Copyright (C) 2004-2009 by Thomas Schüßler
 * Written by Thomas Schüßler <debuglib at atomar dot de>
 * All Rights Reserved
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

if (function_exists('print_a')) :

return;
// debuglib was already included before


else :

if (!defined('USE_DEBUGLIB'))
define('USE_DEBUGLIB', TRUE);

if (!array_key_exists('DEBUGLIB_LVL', $GLOBALS))
$GLOBALS['DEBUGLIB_LVL'] = 99; // 99 debug levels should be enough for everyone
if (!array_key_exists('DEBUGLIB_MAX_Y', $GLOBALS))
$GLOBALS['DEBUGLIB_MAX_Y'] = 50; // how much items per level should get displayed (max_y)
if (!array_key_exists('DEBUGLIB_MAIL_ENCODING', $GLOBALS))
$GLOBALS['DEBUGLIB_MAIL_ENCODING'] = 'utf-8'; // encoding for the mail option
if (!array_key_exists('USE_DEBUGLIB', $GLOBALS))
$GLOBALS['USE_DEBUGLIB'] = USE_DEBUGLIB; // you can set this to TRUE if you have to overwrite the setting in a live environment


$GLOBALS['DbugL_Globals'] = array();
$GLOBALS['DbugL_Globals']['microtime_start'] = microtime(TRUE);
$GLOBALS['DbugL_Globals']['initial_globals_count'] = count($GLOBALS);

class DbugL
{

    public static $first_call = TRUE;

    // shortcuts for the pros
    public static $alt_parameter_names = array(
            'trim_tabs' => array('tt'), 
            'label' => array('l'), 
            'max_y' => array('y'), 
            'return' => array('r'), 
            'window' => array('w'), 
            'window_link' => array('wl'), 
            'debug_level' => array('lvl'), 
            'show_objects' => array('so'), 
            'pickle' => array('p'), 
            'mail' => array('m'), 
            'mail_encoding' => array('me'), 
            'escape_html' => array('e'));

    public static function help($return_mode = FALSE)
    {
        $html = '
                <style type="text/css">
                    div.DbugL_help         {
                        font-family: Verdana, Arial, Helvetica, Geneva, Swiss, SunSans-Regular, sans-serif; font-size:8pt; border:1px dashed black;
                        width:850px;
                        padding:5px;
                    }

                    div.DbugL_help h5 {
                        display:inline;
                        font-size:10pt;
                        border-bottom:1px dotted black;
                    }

                    div.DbugL_help th, div.DbugL_help td {
                        vertical-align:top;
                        text-align:left;
                    }

                    span.new {
                        color:lime;
                        font-weight:bold;
                    }
                </style>

                <div class="DbugL_help">
                    <h5>print_a(mixed input [, string option_string])</h5><br />
                    <br />
                    The function you will use most often, prints a colorful representation of your PHP data<br />
                    <br />
                    $option_string must be in the CSS like syntax:<br />
                    <br />
                    e.g. "max_y:5;window:1;label:my_array"<br />
                    <br />
                    If there is no ":" in the options string, the whole string will be used as the text for the label. <span class="new">new</span><br />
                    <br />
                    Valid options:<br />

                    <table>
                        <tr><th>return:</th>                    <td>(0|1)</td>      <td>Do not print the output and instead return it as a string.</td></tr>
                        <!--<tr><th>help:</th>                      <td>(1)</td>        <td>Show this text.</td></tr>-->
                        <tr><th>label:</th>                     <td>(string)</td>   <td>Draw a fieldset/legend around the output.</td></tr>
                        <tr><th>max_y:</th>                     <td>(1-n)</td>      <td>Maximum number of items on the same level. [...] (defaults to 50)</td></tr>
                        <tr><th>pickle:</th>                    <td>(0|1)</td>      <td>Print a serialized representation of the array instead of printing it as a table.</td></tr>
                        <tr><th>export:</th>                    <td>(0|1)</td>      <td>Print PHP sourcecode of the array. <span class="new">new</span></td></tr>
                        <tr><th>trim_tabs:</th>                 <td>(0-n)</td>      <td>Trim the leading tabs in multiline strings and pad with n tabs.</td></tr>
                        <tr><th>window:</th>                    <td>(string)</td>   <td>The output should open in a new window (javascript), the parameter is also the title for the window.</td></tr>
                        <tr><th>window_link:</th>               <td>(0|1)</td>      <td>Don\'t open a window, just print a link which opens one when clicked. <span class="new">new</span></td></tr>
                        <tr><th>debug_level:</th>               <td>(0-99)</td>     <td>Only do something if the debug level value is <= the global debug level ($GLOBALS[\'DEBUGLIB_LVL\'] default 99) <span class="new">new</span></td></tr>
                        <tr><th></th>                           <td></td>           <td>You can also set it to an array with the levels you want to display e.g. $GLOBALS[\'DEBUGLIB_LVL\'] = array(3,5)</td></tr>
                        <tr><th>avoid@:</th>                    <td>(0|1)</td>      <td>If a key starts with the character "@", assume it is a recursive reference and don\'t follow it.</td></tr>
                        <tr><th>mail:</th>                      <td>(string)</td>   <td>Mail the ouput as HTML mail to the supplied email address. <span class="new">new</span></td></tr>
                        <tr><th>mail_encoding:</th>             <td>(string)</td>   <td>encoding for the HTML mail. (defaults to "utf-8")</td></tr>

                    </table>
                    <br />

                    ' . print_a(self::$alt_parameter_names, 'return:1;label:You can also use these <br />short parameter names:') . '
                    <br />
                    <h5>show_vars([string option_string])</h5><br />
                    <br />
                    Prints all superglobals like $_GET, $_POST, $_SESSION etc. in a big table.<br />
                    Good for printing at the bottom of a page.<br />
                    <br />
                    Options are the same as for print_a + the following options:<br />
                    verbose: also show $_SERVER and $_ENV<br />
                    <br />
                    <!--
                    <h5>print_mysql_result(resource mysql_result[, bool return_mode])</h5><br />
                    <br />
                    Prints a mysql query result as a table<br />
                    <br />
                    -->

                    <h5>script_runtime([bool return_mode [, string title [, string css_style]]] )</h5><br />
                    <br />
                    Prints the passed time since the start of the script (or the last script_runtime call) in seconds.<br />
                    <br />

                    <h5>pre(string string [, string option_string])</h5><br />
                    <br />
                    Print a string so the whitespaces are visible in HTML.<br />
                    <br />

                    option_string must be in the CSS like syntax:<br />
                    <br />
                    eg. "r:1;trim_tabs:0;"<br />
                    <br />
                    Possible options:<br />

                    <table>
                        <tr><th>return:</th>    <td>(0|1)</td> <td>return the output instead of printing it</td></tr>
                        <tr><th>trim_tabs:</th> <td>(0-n)</td> <td>same as in print_a()</td></tr>
                    </table>
                    <br />

                    You can disable the output of all the functions in a production environment by setting <strong>$GLOBALS[\'USE_DEBUGLIB\']</strong> to <strong>FALSE</strong> (e.g. trough auto_prepend in your php.ini).<br />
                    <br />
                    And if you have to do some online debugging you can enable it again somewhere in your script by setting it to <strong>TRUE</strong>.<br />
                </div>
            ';

        if ($return_mode) {
            return $html;
        } else {
            print $html;
        }

    }

    public $window_open_tracker = array();

    private $default_options = array('label' => NULL,
            'window' => NULL, 
            'window_link' => FALSE, 
            'max_y' => 0, 
            'test_for_recursions' => FALSE, 
            'show_objects' => TRUE, 
            'trim_tabs' => NULL, 
            'avoid@' => FALSE, 
            'return' => FALSE, 
            'pickle' => FALSE, 
            'export' => FALSE, 
            'escape_html' => 2, 
            'mail' => NULL, 
            'mail_encoding' => 'utf-8',  // iso-8859-1
            'debug_level' => 0);

    public $options = array();

    public $single_value_flag = FALSE;

    private $element_counter = 0;

    private $window_html;

    private $color_cache = array(); # cache the gradient colors

    private $open_windows = array();

    const runtime_precision = 6; # used for the seconds output of script_runtime()


    const object_key_marker = '<:~!OBJECT_KEY!~:>'; #TODO# hackish


    // default colors for the gradient start
    const key_bg_color_default = '00456A';

    const key_bg_color_array = '10187E';

    const key_bg_color_object = '60008F';

    const key_bg_color_object_data = '60008F';

    const color_step_width = 10;

    public static $css = '
            <style type="text/css" media="screen">

                *.DbugL                { font-family: Verdana, Arial, Helvetica, Geneva, Swiss, SunSans-Regular, sans-serif; }

                pre.DbugL              { display:inline; background:#F1F1F1; font-size:8pt; }
                div.DbugL              { margin-bottom:5px; }

                a.DbugL_window_link    { font-size:xx-small; color:black; border:1px solid darkorange; padding:3px; background:#F1F1F1; margin:2px;}

                div.DbugL_pre          { font-size:8pt; font-family: Verdana, Arial, Helvetica, Geneva, Swiss, SunSans-Regular, sans-serif; margin-bottom:10px; }

                /* Profont is a monospace bitmap font which absolutely rocks! see: http://www.tobias-jung.de/seekingprofont/  */
                span.DbugL_multi       { font-size:9pt; font-family: ProFontWindows, ProFont, Lucida Console, monospace, Courier New; background:#F0F0F9; line-height:100%; }
                span.DbugL_outer_space { background:gold; }
                span.DbugL_tabs        { border-right:1px solid #DDD; }

                /* arrgh.. if someone has a fix for the wrong widths of the fieldsets in IE7 please let me know :| */
                fieldset.DbugL_normal    { display:table-cell; border:1px solid black; padding:2px; }
                fieldset.DbugL_pickled   { width:90%; border:1px solid black; padding:2px; }
                legend.DbugL             { font-size:9pt; font-weight:bold; color:black; }
                div.DbugL_runtime        { font-family: Verdana, Arial, Helvetica, Geneva, Swiss, SunSans-Regular, sans-serif; font-size:9pt; font-weight:normal; color:black; background:yellow; padding:2px; }
                span.DbugL_runtime_label { font-weight:bold; }
                span.DbugL_type_other    { font-family: Verdana, Arial, Helvetica, Geneva, Swiss, SunSans-Regular, sans-serif; font-size:8pt; background:#ECEDFE; color:red;}
                span.DbugL_value_other   { font-family: Verdana, Arial, Helvetica, Geneva, Swiss, SunSans-Regular, sans-serif; font-size:8pt; white-space:nowrap; color:black;}

                table.DbugL                       { background:#D5D5EA; font-size:8pt; border-collapse:separate; }
                table.DbugL th                    { background:#1E32C8; color:white; text-align:left; padding-left:2px; padding-right:2px; font-weight:normal; }
                table.DbugL td                    { background:#DEDEEF; font-weight:normal; }

                table.DbugL th.key_single_value   { background:#FFFF00 !important; color:black !important; font-weight:normal !important; padding:3px;}
                table.DbugL th.key_string         { color:white; }
                table.DbugL th.key_number         { color:green; }
                table.DbugL th.key_array          { color:white; font-weight:bold; }
                table.DbugL th.key_object         { color:white; font-weight:bold; }

                table.DbugL td.value              { padding:0px; }
                table.DbugL td.value_bool_true    { color:#5BA800; padding:1px; }
                table.DbugL td.value_bool_false   { color:#D90062; padding:1px; }
                table.DbugL td.value_string       { color:black; padding:1px; }
                table.DbugL td.value_integer      { color:green; padding:1px; }
                table.DbugL td.value_double       { color:blue; padding:1px; }
                table.DbugL td.value_null         { color:darkorange; padding:1px; }
                table.DbugL td.value_empty_array  { color:darkorange; padding:1px; }
                table.DbugL td.value_empty_string { color:darkorange; padding:1px; }
                table.DbugL td.value_skipped      { color:#666666; padding:1px; }

                div.DbugL_SG                { color:black; font-weight:bold; font-size:9pt; }
                table.DbugL_SG              { width:100%; font-family: Verdana, Arial, Helvetica, Geneva, Swiss, SunSans-Regular, sans-serif;  font-size:8pt; }
                table.DbugL_SG td           { }
                table.DbugL_SG td.globals   { background:#7ACCC8; padding:2px; }
                table.DbugL_SG td.get       { background:#7DA7D9; padding:2px; }
                table.DbugL_SG td.post      { background:#F49AC1; padding:2px; }
                table.DbugL_SG td.files     { background:#82CA9C; padding:2px; }
                table.DbugL_SG td.session   { background:#FCDB26; padding:2px; }
                table.DbugL_SG td.cookie    { background:#A67C52; padding:2px; }
                table.DbugL_SG td.server    { background:#A186BE; padding:2px; }
                table.DbugL_SG td.env       { background:#7ACCC8; padding:2px; }


                div.DbugL_js_hr_first         { width:100%; border-bottom:1px dashed black; margin:10px 0px 10px 0px; font-size:xx-small; text-align:right; background:gold; }
                div.DbugL_js_hr               { width:100%; border-bottom:1px dashed black; margin:10px 0px 10px 0px; font-size:xx-small; text-align:right; background:#EFEFEF }
                div.DbugL_window_content      { padding-top:20px; }
                div.DbugL_window_clear_button { text-align:center; font-size:x-small; position:fixed; top:0px; left:0px; background:orange; width:100%; border-bottom:1px solid black; }

            </style>

            <style type="text/css" media="print">
                table.DbugL_Show_vars {
                    display:none;
                    visibility:invisible;
                }
            </style>
        ';

    public function __construct()
    {

        $this->window_html = '
                <html>
                    <head>
                        ' . self::$css . '
                        <script type="text/javascript">
                            //<![CDATA[

                            var DbugL_test_var = true;

                            function append_html(html) {
                                document.getElementById("content").innerHTML += html;
                                window.scrollTo(0, 1000000);
                                window.focus();
                            }

                            function cls() {
                                document.getElementById("content").innerHTML = "";
                            }

                            //]]>
                        <\/script>
                    </head>
                    <body style="padding:2px;">
                        <div class="DbugL DbugL_window_clear_button" onmouseup="cls();">- clear window -</div>
                        <div id="content" class="DbugL DbugL_window_content"></div>
                    </body>
                </html>
            ';
    }

    // merge user options into default options
    public function set_options($options_string)
    {
        $this->options_string = $options_string;
        $this->default_options['max_y'] = $GLOBALS['DEBUGLIB_MAX_Y'];
        $this->default_options['mail_encoding'] = $GLOBALS['DEBUGLIB_MAIL_ENCODING'];
        $this->options = array_merge($this->default_options, self::parse_options($options_string, DbugL::$alt_parameter_names));
    }

    // wrapper function.. #TODO#
    public static function get_type($value)
    {
        return gettype($value);
    }

    // ouput the css block only if the first time one of the output functions get called
    public static function html_prefix()
    {
        if (self::$first_call) {
            self::$first_call = FALSE;
            return DbugL::$css;
        } else {
            return '';
        }
    }

    // #TODO#
    public static function test_level($level)
    {

        if (is_array($GLOBALS['DEBUGLIB_LVL'])) {
            return in_array($level, $GLOBALS['DEBUGLIB_LVL']);
        } else {
            return $level <= $GLOBALS['DEBUGLIB_LVL'];
        }
    }

    // remove leading tabs
    public static function trim_leading_tabs($string, $tab_padding = NULL)
    {
        /* remove whitespace lines at the start and end of the string */
        $string = preg_replace(array('/^\s*\n/', '/\s*$/'), array('',
                ''), $string);

        // find the smallest leading tab count
        preg_match_all('/^\t+/', $string, $matches);

        if (count($matches[0]) > 0) {
            $min_tab_count = strlen(min($matches[0]));

            // und entfernen
            $string = preg_replace('/^\t{' . $min_tab_count . ',' . $min_tab_count . '}/m', (isset($tab_padding) ? str_repeat("\t", $tab_padding) : ''), $string);
        }

        return $string;
    }

    public static function _handle_whitespace($string)
    {
        // replace 2 or more spaces with nobreaks (for special markup)
        $string = preg_replace_callback('/ {2,}/', create_function('$matches', 'return str_repeat("&nbsp;", strlen($matches[0]));'), $string);

        $string = preg_replace(array('/&nbsp;$/', '/^&nbsp;/'), '<span class="DbugL_outer_space">&nbsp;</span>', $string); # mark spaces at the start/end of the string with red underscores
        $string = str_replace("\t", '&nbsp;&nbsp;<span class="DbugL_tabs">&nbsp;</span>', $string); # replace tabulators with '  »'
        return $string;
    }

    // format strings for output to html
    public static function format_string($string, $trim_tabs = NULL, $escape_html = 2)
    {

        $escape_html == 2 and $string = htmlspecialchars($string);
        if ($escape_html == 0)
        return $string;

        $is_multiline = strpos($string, "\n") !== FALSE;

        if ($is_multiline && isset($trim_tabs)) {
            $string = self::trim_leading_tabs($string, $trim_tabs);
        }

        $string = self::_handle_whitespace($string);
        $string = nl2br($string);
        if ($is_multiline) {
            $string = '<span class="DbugL_multi">' . $string . '</span>';
        }

        return $string;
    }

    // parse the options string in css syntax
    public static function parse_options($options_string = NULL, $alt_parameter_names = array())
    {

        $options = array();

        $alt_parameter_mapping = array();
        foreach ($alt_parameter_names as $parameter_name => $alt_names) {
            $alt_parameter_mapping[$parameter_name] = $parameter_name;
            foreach ($alt_names as $alt_name) {
                $alt_parameter_mapping[$alt_name] = $parameter_name;
            }
        }

        if (!$options_string)
        return $options;

        if (strpos($options_string, ':') !== FALSE) {
            $pairs = explode(';', $options_string);

            foreach ($pairs as &$pair) {

                $pair = trim($pair);
                if ($pair == '')
                continue;

                if (strpos($pair, ':') !== FALSE) {

                    list($option, $value) = explode(':', $pair);

                    if (isset($alt_parameter_mapping[$option])) {
                        $options[$alt_parameter_mapping[$option]] = $value;
                    } else {
                        $options[$option] = $value;
                    }

                }

            }
        } else { // if the options string has no ":" in it, consider it to be the label
            $options['label'] = $options_string;
        }

        return $options;
    }

    #TODO#
    private function _block_s($class = 'null', $title = NULL)
    {
        return '<table ' . (isset($title) ? 'title="' . $title . '" ' : '') . ' cellpadding="0" cellspacing="1" class="DbugL">';
    }

    private function _block_e()
    {
        return '</table>';
    }

    private function _row_s($class = 'null')
    {
        return '<tr>';
    }

    private function _row_e()
    {
        return '</tr>';
    }

    private function _key_s($bg_color, $class, $value)
    {
        $value_type = self::get_type($value);
        if ($this->single_value_flag == TRUE)
        $class .= ' key_single_value';
        if ($value_type == 'array' && count($value) == 0)
        $value_type = 'array (empty)';
        if ($value_type == 'string' && $value == '')
        $value_type = 'string (empty)';
        $html = '<th ' . (isset($class) ? 'class="' . $class . '"' : '') . ' style="background:#' . $bg_color . '" title="' . $value_type . '">';
        if ($this->single_value_flag == TRUE)
        $this->single_value_flag = FALSE;
        return $html;
    }

    private function _key_e()
    {
        return '</th>';
    }

    private function _value_s($class = 'null')
    {
        return '<td class="value ' . $class . '">';
    }

    private function _value_e()
    {
        return '</td>';
    }

    // lighten up the key background color with each iteration
    private function _make_key_bg_color($base_color, $iter)
    {

        if (!isset($this->color_cache[$base_color][$iter])) {
            if ($iter) {
                for ($i = 0; $i < 6; $i += 2) {
                    $component = substr($base_color, $i, 2);
                    $component = hexdec($component);
                    ($component += self::color_step_width * $iter) > 255 and $component = 255;
                    isset($tmp_key_bg_color) or $tmp_key_bg_color = '';
                    $tmp_key_bg_color .= sprintf("%02X", $component);
                }
                $key_bg_color = $tmp_key_bg_color;
            }

            $this->color_cache[$base_color][$iter] = $key_bg_color; // save the color so we dont have to compute it again for this level
        }

        return $this->color_cache[$base_color][$iter];
    }

    // format the key
    private function _format_key($key, $value, $iter, $special_type = NULL)
    {

        $this->element_counter++;

        $key_type = self::get_type($key);
        $value_type = self::get_type($value);

        if (strpos($key, self::object_key_marker) !== FALSE) {
            $key = str_replace(self::object_key_marker, '', $key);
            $value_type = 'OBJECT_DATA';
        }

        switch ($value_type) {

            case 'array' :
                return self::_key_s($this->_make_key_bg_color(self::key_bg_color_array, $iter), 'key_array', $value) . $key . self::_key_e();
                break;

            case 'object' :
                return self::_key_s($this->_make_key_bg_color(self::key_bg_color_object, $iter), 'key_object', $value) . '<span title="Class: ' . (get_class($value)) . '">' . $key . '</span>' . self::_key_e();
                break;

            case 'OBJECT_DATA' :
                return self::_key_s($this->_make_key_bg_color(self::key_bg_color_object_data, $iter), 'key_object_data', $value) . $key . self::_key_e();
                break;

            default :

                return self::_key_s($this->_make_key_bg_color(self::key_bg_color_default, $iter), NULL, $value) . self::format_string($key, $this->options['trim_tabs']) . self::_key_e();
                break;
        }

        return self::_key_s('key_string') . $key . self::_key_e();
    }

    // format the value
    private function _format_value($value)
    {

        $value_type = self::get_type($value);

        switch ($value_type) {

            case 'boolean' :
                if ($value == TRUE) {
                    return self::_value_s('value_bool_true') . 'TRUE' . self::_value_e();
                } else {
                    return self::_value_s('value_bool_false') . 'FALSE' . self::_value_e();
                }
                break;

            case 'string' :
                if ($value == '') {
                    return self::_value_s('value_empty_string') . "''" . self::_value_e();
                } else {
                    return self::_value_s('value_string') . self::format_string($value, $this->options['trim_tabs'], $this->options['escape_html']) . self::_value_e();
                }
                break;

            case 'integer' :
                return self::_value_s('value_integer') . $value . self::_value_e();
                break;

            case 'double' :
                return self::_value_s('value_double') . $value . self::_value_e();
                break;

            case 'NULL' :
                return self::_value_s('value_null') . 'NULL' . self::_value_e();
                break;

            case 'array' :
                return self::_value_s('value_empty_array') . '[]' . self::_value_e();
                break;

            case 'object' :
                return self::_value_s('value_empty_array') . '{}' . self::_value_e();
                break;
        }
    }

    // called by the global interface function
    public function print_a($input, &$html, $iter = 0)
    {

        $iter++;

        $input_type = self::get_type($input);

        // input was neither an array nor an object
        if (!in_array($input_type, array(
                'array', 
                'object'))) {
        if ($input_type == 'resource') {
            $html = '<span nowrap="nowrap" class="DbugL_value_other"><span class="DbugL_type_other">(' . $input_type . ')</span> ' . $input . '</span>';
        } else {
            $html = '<span nowrap="nowrap" class="DbugL_value_other"><span class="DbugL_type_other">(' . $input_type . ')</span> ' . self::format_string($input, $this->options['trim_tabs']) . '</span>';
        }
        return;
                }

                if ($iter == 1) {
                    $bt = debug_backtrace();
                    $bt = $bt[1];
                    $title = "({$bt['line']}) {$bt['file']}";
                    #TODO# .(isset($bt['function']) ? '    function:'.$bt['function'] : '');
                } else {
                    $title = NULL;
                }

                $html .= self::_block_s(NULL, $title);

                $loop_i = 0;
                foreach ($input as $key => $value) {
                    $html .= self::_row_s();

                    if ($this->options['max_y'] > 0 && $loop_i >= $this->options['max_y']) {
                        $html .= $this->_format_key('...', $value, $iter);
                        $html .= self::_value_s('value_skipped') . '<span title="' . (count($input) - $loop_i) . ' items at this level were skipped (max_y=' . $this->options['max_y'] . ').">[' . (count($input) - $loop_i) . '&nbsp;skipped]</span>' . self::_value_e();
                        break;
                    }

                    $html .= $this->_format_key($key, $value, $iter);

                    if ($this->options['avoid@'] == '1' && $key[0] == '@') {
                        $html .= $this->_format_value('Recursion');

                    } elseif (is_array($value) && !empty($value)) {

                        $html .= self::_value_s();
                        $this->print_a($value, $html, $iter);
                        $html .= self::_value_e();

                    } elseif (is_object($value)) {
                        $html .= self::_value_s();
                        if ($this->options['show_objects']) {
                            $this->print_a($value, $html, $iter);
                        } else {
                            $html .= '<span title="not shown due to the option &quot;show_objects:0&quot;">...</span>';
                        }
                        $html .= self::_value_e();

                    } else {

                        $html .= $this->_format_value($value);

                    }

                    $html .= self::_row_e();

                    $loop_i++;
                }

                $html .= self::_block_e();

    }

    // called by the global interface function
    public function js_for_popup($html)
    {

        $title = $this->options['window'];
        $window_name = 'DbugL_' . md5($_SERVER['HTTP_HOST']) . '_' . $title;

        if (array_key_exists($window_name, $this->window_open_tracker)) {
            $hr_class = 'DbugL_js_hr';
        } else {
            $this->window_open_tracker[$window_name] = TRUE;
            $hr_class = 'DbugL_js_hr_first';
        }

        $print_css = in_array($window_name, $this->open_windows) ? FALSE : TRUE;

        $this->open_windows[] = $window_name;

        $debugwindow_origin = $_SERVER["HTTP_HOST"] . $_SERVER["REQUEST_URI"];

        $html = self::escape_js($html);

        return '

                ' . ($this->options['window_link'] ? '<a class="DbugL_window_link" href="javascript:open_' . $window_name . '();">print_a( $array, ' . $this->options_string . ' )</a>' : '') . '

                <script type="text/javascript">

                    //<![CDATA[

                        ' . ($this->options['window_link'] ? 'function open_' . $window_name . '() {' : '') . '

                            try {

                                if(window["' . $window_name . '"] == undefined) {
                                    window["' . $window_name . '"] = window.open("", "W' . $window_name . '", "menubar=no,scrollbars=yes,resizable=yes,width=640,height=480");
                                }

                                if(window["' . $window_name . '"].DbugL_test_var == undefined) {
                                    with (window["' . $window_name . '"].document) {
                                        open();
                                        write("' . self::escape_js($this->window_html) . '");
                                        close();
                                        title = "' . $title . ' Debugwindow for : http(s)://' . $debugwindow_origin . '";
                                    }
                                }

                                window["' . $window_name . '"].append_html("<div class=\"' . $hr_class . '\">"+ new Date().toLocaleString() +"</div>");
                                window["' . $window_name . '"].append_html("' . $html . '");

                            } catch(e) {
                                if(window["DbugL_popup_flag"] == undefined) {
                                    alert("print_a() could not open window. Please enable popups!");
                                }
                            }

                        ' . ($this->options['window_link'] ? '}' : '') . '

                    //]]>

                </script>
        ';

    }

    public static function escape_js($string)
    {
        $string = str_replace(array("\r\n", "\n", "\r"), '\n', $string);
        $string = str_replace('</', '<\\/', $string);
        $string = str_replace('"', '\"', $string);
        return $string;
    }

    public function get_html($html)
    {
        if (isset($this->options['label'])) {
            if ($this->options['pickle'] == TRUE || $this->options['export'] == TRUE) {
                $html = '<form"><fieldset class="DbugL_pickled"><legend class="DbugL">' . $this->options['label'] . '</legend>' . $html . '</fieldset></form>';
            } else {
                $html = '<form style="display:table;"><fieldset class="DbugL_normal"><legend class="DbugL">' . $this->options['label'] . '</legend>' . $html . '</fieldset></form>';
            }

        }
        $html = '<div class="DbugL">' . $html . '</div>';
        return $html;
    }

    // used by show_vars to get all globals defined in a script (minus the core PHP stuff)
    public static function script_globals()
    {
        $varcount = 0;
        $script_globals = array();

        foreach ($GLOBALS as $variable_name => $variable_value) {
            if (++$varcount > $GLOBALS['DbugL_Globals']['initial_globals_count']) {

                /* die wollen wir nicht! */
                if ($variable_name != 'HTTP_SESSION_VARS' && $variable_name != '_SESSION') {
                    $script_globals[$variable_name] = $variable_value;
                }

            }
        }

        unset($GLOBALS['DbugL_Globals']['initial_globals_count']);
        return $script_globals;
    }

} // DbugL


// ------------------------------- here come the global user functions ------------------------------- //


function pre($string, $options_string = NULL)
{
    if (!$GLOBALS['USE_DEBUGLIB'])
    return;

    $options = DbugL::parse_options($options_string);

    if (isset($options['debug_level']) && $options['debug_level'] > $GLOBALS['DEBUGLIB_LVL'])
    return;

    if (isset($options['trim_tabs'])) {
        $string = DbugL::trim_leading_tabs($string, $options['trim_tabs']);
    }

    $html = DbugL::html_prefix() . '<div class="DbugL_pre"><span>' . DbugL::format_string($string) . '</span></div>';

    if (isset($options['return']) && $options['return'] == '1') {
        return $html;
    } else {
        print $html;
    }
}

function pre_die($string, $options_string = NULL)
{
    if (!$GLOBALS['USE_DEBUGLIB'])
    return;

    pre($string, $options_string);
    die();
}

// shows time elapsed since last call
function script_runtime($label = '', $style = '', $return_mode = FALSE)
{

    $bt = debug_backtrace();
    $bt = $bt[1];
    $title = "({$bt['line']}) {$bt['file']}";

    if ($label != '')
    $label = '<span class="DbugL_runtime_label">[' . $label . ']</span> ';

    if (!$GLOBALS['USE_DEBUGLIB'])
    return;

    $now_time = microtime(TRUE);
    if (isset($GLOBALS['DbugL_Globals']['last_microtime'])) {
        $step_time = $now_time - $GLOBALS['DbugL_Globals']['last_microtime'];
    }
    $GLOBALS['DbugL_Globals']['last_microtime'] = $now_time;

    $elapsed_time = sprintf('%01.' . DbugL::runtime_precision . 'f', $now_time - $GLOBALS['DbugL_Globals']['microtime_start']);

    $html = DbugL::html_prefix() . '<div class="DbugL_runtime" style="' . $style . '" title="' . $title . '">' . $label . ' time: ' . $elapsed_time . (isset($step_time) ? ' (' . sprintf('%01.' . DbugL::runtime_precision . 'f', $step_time) . ' since last call)' : '') . '</div>';

    if ($return_mode) {
        return $html;
    } else {
        print $html;
    }
}

// the interface function for Debuglib::_print_a()
function print_a($input, $options_string = NULL)
{
    if (!$GLOBALS['USE_DEBUGLIB'])
    return;

    static $DbugL;

    if (!$DbugL)
    $DbugL = new DbugL();
    $DbugL->set_options($options_string);

    if (!is_array($input) && !is_object($input) || (is_array($input) && count($input) == 0)) {
        $DbugL->single_value_flag = TRUE; #hackish
        $input = array('(' . gettype($input) . ')' => $input);
    }

    if (!Dbugl::test_level($DbugL->options['debug_level']))
    return;

    $html = '';

    // use print_r() to check for a recursion in the structure
    if ($DbugL->options['test_for_recursions'] && strpos(print_r($input, 1), '*RECURSION*') !== FALSE) {
        $html = 'RECURSION detected!';
    } else {
        $DbugL->print_a($input, $html);
    }

    // open a window for the output?
    if (isset($DbugL->options['window'])) {

        if ($DbugL->options['pickle'] == TRUE) {

            $pickled_input = serialize($input);
            $pickled_input = str_replace("'", "\\\'", $pickled_input);
            $pickled_input = '<textarea style="width:100%;height:200px;">' . $pickled_input . '</textarea>';
            $html = $DbugL->js_for_popup($DbugL->get_html($pickled_input));

        } elseif ($DbugL->options['export'] == TRUE) {

            $export_input = var_export($input, TRUE);
            $export_input = '<textarea style="width:100%;height:200px;">' . $export_input . '</textarea>';
            $html = $DbugL->js_for_popup($DbugL->get_html($export_input));

        } else {
            $html = $DbugL->js_for_popup($DbugL->get_html($html));
        }
    } else {
        $html = DbugL::html_prefix() . $DbugL->get_html($html);
    }

    if (isset($DbugL->options['help'])) {
        $html = DbugL::help($DbugL->options['return']);
    }

    if ($DbugL->options['mail']) {
        $headers = 'MIME-Version: 1.0' . "\r\nContent-type: text/html; charset=" . $DbugL->options['mail_encoding'] . "\r\nFrom: debuglib\r\n";
        $message = '<html><head><title>debuglib.php @ ' . $_SERVER['HTTP_HOST'] . '</title></head><body>' . DbugL::$css . $html . '</body></html>';
        mail($DbugL->options['mail'], 'debuglib.php @ ' . $_SERVER['HTTP_HOST'], $message, $headers);
    } elseif ($DbugL->options['return'] == '1') {
        return $html;
    } else {
        print $html;
    }

}

// call print_a() and die (RIP)
function print_a_die($input, $options_string = NULL)
{
    if (!$GLOBALS['USE_DEBUGLIB'])
    return;

    print_a($input, $options_string);
    die();
}

// deprecated
function die_a($input, $options_string = NULL)
{
    if (!$GLOBALS['USE_DEBUGLIB'])
    return;

    print_a($input, $options_string);
    die();
}

// good for printing all kind of superglobals at the bottom of a page
function show_vars($options_string = NULL)
{
    if (!$GLOBALS['USE_DEBUGLIB'])
    return;

    $options = DbugL::parse_options($options_string, DbugL::$alt_parameter_names);

    $print_a_options = $options_string . ';return:1;';

    $superglobals = array('Script $GLOBALS' => DbugL::script_globals(),
            '$_GET' => $_GET, 
            '$_POST' => $_POST, 
            '$_FILES' => $_FILES, 
            '$_SESSION' => $_SESSION, 
            '$_COOKIE' => $_COOKIE);

    if (isset($options['verbose']) && $options['verbose'] == '1') {
        $superglobals['$_SERVER'] = $_SERVER;
        $superglobals['$_ENV'] = $_ENV;
    }

    $html = DbugL::html_prefix() . script_runtime('before show_vars', 'background:#BBB;', TRUE, TRUE);
    $html .= '<table class="DbugL_SG" cellpadding="0" cellspacing="0">';

    foreach ($superglobals as $name => $reference) {
        if (empty($reference))
        continue;
        $class_name = $name == 'Script $GLOBALS' ? 'globals' : strtolower(str_replace('$_', '', $name));
        $html .= '<tr><td class="' . $class_name . '"><div class="DbugL_SG">' . $name . '</div>';
        $html .= print_a($reference, $print_a_options);
        $html .= '</td></tr>';
    }

    $html .= '</table>' . script_runtime('after show_vars', 'background:#BBB;', TRUE, TRUE);

    if (@$options['return'] == '1') {
        return $html;
    } else {
        print $html;
    }

}

// prints out a mysql result.. work in progress.. use at your own risk
function print_mysql_result($mysql_result, $return_mode = FALSE)
{
    if (!$GLOBALS['USE_DEBUGLIB'])
    return;

    if (!$mysql_result || mysql_num_rows($mysql_result) < 1)
    return;

    $field_count = mysql_num_fields($mysql_result);

    $tables = array();

    for ($i = 0; $i < $field_count; $i++) {
        if (isset($tables[mysql_field_table($mysql_result, $i)])) {
            $tables[mysql_field_table($mysql_result, $i)]++;
        } else {
            $tables[mysql_field_table($mysql_result, $i)] = 1;
        }
    }

    $html = '
            <style type="text/css">
                table.DbugL_PR           { font-family: Verdana, Arial, Helvetica, Geneva, Swiss, SunSans-Regular, sans-serif; background:black; margin-bottom:10px; }
                table.DbugL_PR th.t_name { font-size:9pt; font-weight:bold; color:white; }
                table.DbugL_PR th.f_name { font-size:7pt; font-weight:bold; color:white; }
                table.DbugL_PR td        { padding-left:2px;font-size:7pt; white-space:nowrap; vertical-align:top; }
            </style>
            <script type="text/javascript">
                //<![CDATA[

                var DbugL_last_id;
                function DbugL_highlight(id) {
                    if(DbugL_last_id) {
                        DbugL_last_id.style.color = "#000000";
                        DbugL_last_id.style.textDecoration = "none";
                    }
                    var highlight_td;
                    highlight_td = document.getElementById(id);
                    highlight_td.style.color ="#FF0000";
                    highlight_td.style.textDecoration = "underline";
                    DbugL_last_id = highlight_td;
                }

                //]]>
            </script>
        ';

    $html .= '<table class="DbugL_PR" cellspacing="1" cellpadding="1">';
    $html .= '<tr>';
    foreach ($tables as $tableName => $tableCount) {
        (!isset($col) || $col == '#006F05') ? $col = '#00A607' : $col = '#006F05';
        $html .= '<th colspan="' . $tableCount . '" class="t_name" style="background:' . $col . ';">' . $tableName . '</th>';
    }
    $html .= '</tr>';

    $html .= '<tr>';
    for ($i = 0; $i < mysql_num_fields($mysql_result); $i++) {
        $field = mysql_field_name($mysql_result, $i);
        $col == '#0054A6' ? $col = '#003471' : $col = '#0054A6';
        $html .= '<th style="background:' . $col . ';" class="f_name">' . $field . '</th>';
    }
    $html .= '</tr>';

    mysql_data_seek($mysql_result, 0);

    $toggle = FALSE;
    $pointer = 0;

    $table_id = str_replace('.', '', microtime(TRUE));
    while ($db_row = mysql_fetch_array($mysql_result, MYSQL_NUM)) {
        $pointer++;
        if ($toggle) {
            $col1 = "#E6E6E6";
            $col2 = "#DADADA";
        } else {
            $col1 = "#E1F0FF";
            $col2 = "#DAE8F7";
        }

        $toggle = !$toggle;
        $id = 'DbugL_' . $table_id . '_' . $pointer;
        $html .= '<tr id="' . $id . '" onMouseDown="DbugL_highlight(\'' . $id . '\');">';
        foreach ($db_row as $i => $value) {
            $col == $col1 ? $col = $col2 : $col = $col1;
            $flags = mysql_field_flags($mysql_result, $i);
            $primary_flag = strpos($flags, 'primary_key') !== FALSE;
            $html .= '<td style="background:' . $col . ';' . ($primary_flag ? 'font-weight:bold;' : '') . '" nowrap="nowrap">' . nl2br($value) . '</td>';
        }
        $html .= '</tr>';
    }
    $html .= '</table>';
    mysql_data_seek($mysql_result, 0);

    if ($return_mode) {
        return $html;
    } else {
        print $html;
    }
}


endif;

#TODO#
/*
 inject css through javascript (inline style is bad... m'kay?)
 unify options parsing
 debuglevel and default parameter arrays for every function
 fix fieldset width in IEx
 debug backtrace issues
 write a C extension that can test for reference recursions
 start from scratch :)
 */